package org.xelnaga.defiler.web.controller.contentlet;

import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.seam.wiki.core.action.prefs.WikiPreferences;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.xelnaga.defiler.context.AccountContext;
import org.xelnaga.defiler.domain.CompanyModel;
import org.xelnaga.defiler.domain.ContentletModel;
import org.xelnaga.defiler.domain.FieldModel;
import org.xelnaga.defiler.domain.FieldModel.FieldTypeEnum;
import org.xelnaga.defiler.domain.FieldWrapper;
import org.xelnaga.defiler.domain.FileAssetModel;
import org.xelnaga.defiler.domain.FolderModel;
import org.xelnaga.defiler.domain.SiteModel;
import org.xelnaga.defiler.domain.StructureModel;
import org.xelnaga.defiler.service.ContentletService;
import org.xelnaga.defiler.service.FieldService;
import org.xelnaga.defiler.service.FolderService;
import org.xelnaga.defiler.service.SiteService;
import org.xelnaga.defiler.service.StructureService;
import org.xelnaga.defiler.web.controller.fileasset.FileAssetMessage;
import org.xelnaga.defiler.web.controller.fileasset.FileAssetWebSupport;
import org.xelnaga.defiler.web.controller.folder.FolderMessage;

/**
 * This controller handles create and update operations for contentlet.
 * It exposes to the servlet dispatcher a contentletForm instance that holds a non
 * connected (in the hibernate sens) contentlet instance.
 * View returned by the handler methods can be configured.
 * 
 * Note: This controller name is forced only to minimize the risks of name collisions with
 * controllers written by hands. The name is anyway not used since controllers sit on top 
 * of everything else. 
 */
@Controller("generatedContentletFormController")
@RequestMapping({
        "/domain/contentlet/createForm",
        "/domain/contentlet/create",
        "/domain/contentlet/createFile",
        "/domain/contentlet/updateForm",
        "/domain/contentlet/update",
        "/domain/contentlet/updateContentForm",
        "/domain/contentlet/updateContent"        
})
public class ContentletFormController {
    private Log logger = LogFactory.getLog(ContentletFormController.class);
    //----------------------------------------------------
    // Dependency Injection
    //----------------------------------------------------
    
    @Autowired
    protected ContentletFormValidator contentletFormValidator;
    @Autowired
    protected ContentletFormService contentletFormService;
    @Autowired
    protected ContentletService contentletService;
    @Autowired
    protected StructureService structureService;
    @Autowired
    private ContentletWebSupport contentletWebSupport;
    @Autowired
    private FileAssetWebSupport fileAssetWebSupport;
    @Autowired
    private WikiPreferences wikiPreferences;
    @Autowired
    private FolderService folderService;
    @Autowired
    private FieldService fieldService;
    @Autowired
    private SiteService siteService;
    @InitBinder
    public void initDataBinder(WebDataBinder binder){
        // you can explicitly allow certain fields or exclude them
        // binder.setAllowedFields(new String[]{"contentlet.inode", "contentlet.live", "contentlet.working", "contentlet.deleted", "contentlet.locked", "contentlet.showOnMenu", "contentlet.title", "contentlet.modDate", "contentlet.modUser", "contentlet.sortOrder", "contentlet.friendlyName", "contentlet.languageId", "contentlet.structureInode", "contentlet.lastReview", "contentlet.nextReview", "contentlet.reviewInterval", "contentlet.disabledWysiwyg", "contentlet.host", "contentlet.folderId", "contentlet.date1", "contentlet.date2", "contentlet.date3", "contentlet.date4", "contentlet.date5", "contentlet.date6", "contentlet.date7", "contentlet.date8", "contentlet.date9", "contentlet.date10", "contentlet.date11", "contentlet.date12", "contentlet.date13", "contentlet.date14", "contentlet.date15", "contentlet.date16", "contentlet.date17", "contentlet.date18", "contentlet.date19", "contentlet.date20", "contentlet.date21", "contentlet.date22", "contentlet.date23", "contentlet.date24", "contentlet.date25", "contentlet.text1", "contentlet.text2", "contentlet.text3", "contentlet.text4", "contentlet.text5", "contentlet.text6", "contentlet.text7", "contentlet.text8", "contentlet.text9", "contentlet.text10", "contentlet.text11", "contentlet.text12", "contentlet.text13", "contentlet.text14", "contentlet.text15", "contentlet.text16", "contentlet.text17", "contentlet.text18", "contentlet.text19", "contentlet.text20", "contentlet.text21", "contentlet.text22", "contentlet.text23", "contentlet.text24", "contentlet.text25", "contentlet.textArea1", "contentlet.textArea2", "contentlet.textArea3", "contentlet.textArea4", "contentlet.textArea5", "contentlet.textArea6", "contentlet.textArea7", "contentlet.textArea8", "contentlet.textArea9", "contentlet.textArea10", "contentlet.textArea11", "contentlet.textArea12", "contentlet.textArea13", "contentlet.textArea14", "contentlet.textArea15", "contentlet.textArea16", "contentlet.textArea17", "contentlet.textArea18", "contentlet.textArea19", "contentlet.textArea20", "contentlet.textArea21", "contentlet.textArea22", "contentlet.textArea23", "contentlet.textArea24", "contentlet.textArea25", "contentlet.integer1", "contentlet.integer2", "contentlet.integer3", "contentlet.integer4", "contentlet.integer5", "contentlet.integer6", "contentlet.integer7", "contentlet.integer8", "contentlet.integer9", "contentlet.integer10", "contentlet.integer11", "contentlet.integer12", "contentlet.integer13", "contentlet.integer14", "contentlet.integer15", "contentlet.integer16", "contentlet.integer17", "contentlet.integer18", "contentlet.integer19", "contentlet.integer20", "contentlet.integer21", "contentlet.integer22", "contentlet.integer23", "contentlet.integer24", "contentlet.integer25", "contentlet.float1", "contentlet.float2", "contentlet.float3", "contentlet.float4", "contentlet.float5", "contentlet.float6", "contentlet.float7", "contentlet.float8", "contentlet.float9", "contentlet.float10", "contentlet.float11", "contentlet.float12", "contentlet.float13", "contentlet.float14", "contentlet.float15", "contentlet.float16", "contentlet.float17", "contentlet.float18", "contentlet.float19", "contentlet.float20", "contentlet.float21", "contentlet.float22", "contentlet.float23", "contentlet.float24", "contentlet.float25", "contentlet.bool1", "contentlet.bool2", "contentlet.bool3", "contentlet.bool4", "contentlet.bool5", "contentlet.bool6", "contentlet.bool7", "contentlet.bool8", "contentlet.bool9", "contentlet.bool10", "contentlet.bool11", "contentlet.bool12", "contentlet.bool13", "contentlet.bool14", "contentlet.bool15", "contentlet.bool16", "contentlet.bool17", "contentlet.bool18", "contentlet.bool19", "contentlet.bool20", "contentlet.bool21", "contentlet.bool22", "contentlet.bool23", "contentlet.bool24", "contentlet.bool25"});
        binder.registerCustomEditor(java.util.Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), true));
    }

    /**
     * Create an ContentletForm instance and in the case where an id is present, populate it
     * with raw data coming from the database. The form is accessible from the view
     * as the attribute "contentletForm".
     * 
     * Note: Since this method is annotated with @ModelAttribute, it will be executed
     * before the chosen @RequestMapping annotated handler method. In particular, when 
     * the update method handler is invoked, the form is first populated by
     * this method and then its content is overridden by the data posted by the end-user.
     */
    @ModelAttribute
    protected ContentletForm populateForm(@ModelAttribute ContentletForm contentletForm) {
        if (logger.isDebugEnabled()) {
            logger.debug("Received contentlet: " + contentletForm.getContentlet());
        }
		Long contentletInode = contentletForm.getContentlet().getInode();
        ContentletModel contentlet = contentletService.getByPrimaryKey(contentletInode);
        if (contentlet != null) {
            // update form with a disconnected version of contentlet
        	contentletForm.setContentlet(contentlet);
        } else {
        	Long structureInode = contentletForm.getContentlet().getStructureInode();
        	contentletForm.getContentlet().setStructure(structureService.getByPrimaryKey(structureInode));
        }
        return contentletForm;
    }

    /**
     * Simply returns the createFormViewName that is responsible for rendering the contentletForm.
     * @return the createFormViewName
     */
    @RequestMapping
    public String createForm(@ModelAttribute ContentletForm contentletForm, Model model) {
    	referenceData(contentletForm, model);
        return createFormViewName;
    }

    protected void referenceData(ContentletForm contentletForm, Model model) {
//    	model.addAttribute("contentletForm", this.contentletFormService.populateForm(contentletForm));
        Long structureInode = contentletForm.getContentlet().getStructureInode();
		//The structure search field.
        List<FieldModel> fields = fieldService.getByStructureInode(structureInode);
        List<FieldWrapper> wrappers = contentletWebSupport.getContentletFieldValues(contentletForm.getContentlet());//new ArrayList<FieldWrapper>();
//        for(FieldModel field : fields) {
//        	//Process text_area field.
//			if(field.getFieldContentlet().contains("_")) {
//				String[] array = field.getFieldContentlet().split("_");
//				if(array.length == 2) {//handl text_area1
//					field.setFieldContentlet(array[0] + StringUtils.capitalize(array[1]));
//				}
//			}
//        	wrappers.add(new FieldWrapper(field, 
//        			contentletForm.getContentlet().getFieldValue(field.getFieldContentlet())));
//        }
        model.addAttribute("fields", wrappers);
        model.addAttribute("categoryMap", contentletWebSupport.getStructureFieldCategories(fields));
        
        List<SiteModel> siteList = siteService.find();
        List<FolderMessage> folders = new ArrayList<FolderMessage>();
        for(SiteModel site : siteList) {
        	List<FolderModel> children = folderService.getListAllBySiteInode(site.getInode());
        	for(FolderModel f : children) {
        		FolderMessage message = new FolderMessage(f);
        		message.setDepth(f.getLevel().intValue());
        		folders.add(message);
        	}
        }
        model.addAttribute("folders", folders);
	}

//	private List<FolderModel> getSiteFolderTreeList(SiteModel site) {
//		List<FolderModel> folderList = this.folderService.getListAllBySiteInode(site.getInode());
//		List<FolderModel> result = new ArrayList<FolderModel>();
//		for(FolderModel top : folderList) {
//			if(null == top.getParentNode()) {
//				TreeNode<FolderModel> treeNode = new CacheableTreeNodeImpl<FolderModel>(top);
//				TreeModelBuilder.constructFolderTreeNode(treeNode, folderList);
//				result.addAll(TreeModelBuilder.constructFolderTreeList(treeNode));
//			}
//		}
//		return result;
//	}

	@RequestMapping
    public String create(HttpServletRequest request, @ModelAttribute ContentletForm contentletForm, 
    		BindingResult result, Model model) {
    	contentletForm.getContentlet().initDefaultValues();
    	contentletFormValidator.validate(contentletForm, result);
    	
    	if(result.hasErrors()) {
    		referenceData(contentletForm, model);
    		return createFormViewName;
    	}
    	@SuppressWarnings("unchecked")
		Map<String, String[]> paramMap = request.getParameterMap();
    	StructureModel structure = contentletForm.getContentlet().getStructure();
    	Map<FieldModel, String[]> categoriesValue = processCategoryFields(paramMap, structure);

    	FileAssetModel coverFile = fileAssetWebSupport.getUploadedFileAsset(contentletForm.getCoverFileAsset());
    	contentletForm.getContentlet().setFileAsset(coverFile);
    	FileAssetModel icon = fileAssetWebSupport.getUploadedFileAsset(contentletForm.getIconFileAsset());
    	contentletForm.getContentlet().setIconFileAsset(icon);
    	
    	CompanyModel company = AccountContext.getAccountContext().getAccount().getCompany();
		contentletForm.getContentlet().setCompany(company);
    	contentletFormService.create(contentletForm, categoriesValue);
    	
    	bind(model, contentletForm);
    	return createViewName;
    }
    
    /**
     * Load the multi select category value.
     * @param paramMap
     * @param structure
     * @return
     */
	private Map<FieldModel, String[]> processCategoryFields(
			Map<String, String[]> paramMap, StructureModel structure) {
    	if(null != structure) {
    		Map<FieldModel, String[]> result = new HashMap<FieldModel, String[]>();
    		List<FieldModel> fields = this.fieldService.getByStructureInode(structure.getInode());
    		for(FieldModel field : fields) {
    			FieldTypeEnum typeEnum = FieldTypeEnum.getInstance(field.getFieldType());
    			if(null != typeEnum && 
    					(typeEnum == FieldTypeEnum.CATEGORY || typeEnum == FieldTypeEnum.MULTICATEGORY)) {
    				String property = "contentlet." + field.getFieldContentlet();
    				if(field.getFieldContentlet().contains("_")) {
    					String[] array = field.getFieldContentlet().split("_");
    					if(array.length == 2) {//handl text_area1
    						property = "contentlet." + array[0] + StringUtils.capitalize(array[1]);
    					}
    				}
    				result.put(field, paramMap.get(property));
    			}
    		}
    		return result;
    	}
		return null;
	}

	/**
     * 编辑自定义内容时，保存上传的图片
     * Validates the incoming contentletForm and if validation passes, creates a new ContentletModel instance.
     */
    @RequestMapping
    public String createFile(HttpServletRequest request, @ModelAttribute ContentletForm contentletForm, BindingResult result, Model model) {
    	ContentletModel contentlet = contentletForm.getContentlet();
        FileAssetModel fileAssetModel = fileAssetWebSupport.getUploadedFileAsset(contentletForm.getFlashUploadFiles());
    	StructureModel structure = contentlet.getStructure();
    	String imgPath = "/" + FolderModel.SystemFolderEnum.Img.name().toLowerCase() + "/" + structure.getVelocityVarName();
		FolderModel parentFolder = folderService.getBySiteAndPath(contentlet.getFolder().getSiteInode(), imgPath);
		fileAssetModel.setParentNode(parentFolder);
		fileAssetModel.setModDate(new Date());
		fileAssetModel.setModUser(AccountContext.getAccountContext().getUsername());
        // create
		contentletService.saveWithIlluFileAsset(contentlet, fileAssetModel);
		FileAssetMessage mediaMessage = new FileAssetMessage();
		mediaMessage.setHeight(fileAssetModel.getHeight());
		mediaMessage.setWidth(fileAssetModel.getWidth());
		String imgHttpBasePath = wikiPreferences.getAdminImgHttpBasePath();
		mediaMessage.setFilePath(imgHttpBasePath + fileAssetModel.getPath());
		mediaMessage.setName(imgHttpBasePath + fileAssetModel.getPath());
        //保存图片到磁盘。
        try {
			fileAssetWebSupport.storeMedia(fileAssetModel, contentletForm.getFlashUploadFiles());
		} catch (Exception e) {
			e.printStackTrace();
			mediaMessage.setStatus(FileAssetMessage.STATUS_FAILURE);
		}
		if(null == mediaMessage.getStatus()) {
			mediaMessage.setStatus(FileAssetMessage.STATUS_SUCCESS);
		}
		model.addAttribute("mediaMessage", mediaMessage);
		return "domain/contentlet/createFileResult";
    }

    /**
     * Simply return the upadteFormViewName that is reponsible for rendering the contentletForm.
     * @return the updateFormViewName
     */
    @RequestMapping
    public String updateForm(ContentletForm contentletForm, Model model) {
    	this.referenceData(contentletForm, model);
        return updateFormViewName;
    }

    /**
     * Validates the incoming contentletForm and if validation passes, update the corresponding ContentletModel instance.
     */
    @RequestMapping
    public String update(@ModelAttribute ContentletForm contentletForm, BindingResult result, Model model) {
        // validate the contentletForm
        contentletFormValidator.validate(contentletForm, result);
        if (result.hasErrors()) {
        	this.referenceData(contentletForm, model);
            return updateFormViewName;
        }
        contentletForm.getContentlet().setModDate(new Date());
        contentletForm.getContentlet().setModUser(AccountContext.getAccountContext().getUsername());
        // update
        contentletService.update(contentletForm.getContentlet());
        // bind the updated contentlet instance to the view
        bind(model, contentletForm);

        // return the view name
        return updateViewName;
    }

    @RequestMapping
    public String updateContentForm() {
        return updateContentFormViewName;
    }
    
    @RequestMapping
    public String updateContent(HttpServletRequest request, @ModelAttribute ContentletForm contentletForm, BindingResult result, Model model) {
    	try {
			InputStream input = request.getInputStream();
			if(null != input) {
				contentletForm.getContentlet().setContent(IOUtils.toString(input));
				contentletForm.getContentlet().setModDate(new Date());
				contentletForm.getContentlet().setModUser(AccountContext.getAccountContext().getUsername());
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
    	return update(contentletForm, result, model);
    }    
    /**
     * Bind the passed contentlet instance and its id to the passed model.
     */
    protected void bind(Model model, ContentletForm contentletForm) {
    	ContentletModel contentlet = contentletForm.getContentlet();
        // in case the view is a redirect
        model.addAttribute("contentlet.inode", contentlet.getInode()); 
        
        if(null != contentlet.getFileAsset() 
        		&& null != contentletForm.getCoverFileAsset()
        		&& contentletForm.getCoverFileAsset().getSize() > 0) {
        	fileAssetWebSupport.storeMedia(contentlet.getFileAsset(), contentletForm.getCoverFileAsset());
        }
        if(null != contentlet.getIconFileAsset() 
        		&& null != contentletForm.getIconFileAsset()
        		&& contentletForm.getIconFileAsset().getSize() > 0) {
          	fileAssetWebSupport.storeMedia(contentlet.getIconFileAsset(), contentletForm.getIconFileAsset());
        }
    }
    
    //----------------------------------------------------
    // Returned view configuration
    //----------------------------------------------------

    private String createFormViewName = "domain/contentlet/createForm";

    public void setCreateFormViewName(String createFormViewName) {
        this.createFormViewName = createFormViewName ;
    }

    private String createViewName = "redirect:/domain/contentlet/show";

    public void setCreateViewName(String createViewName) {
        this.createViewName = createViewName ;
    }

    private String updateFormViewName = "domain/contentlet/updateForm";
    private String updateContentFormViewName = "domain/contentlet/updateContentForm";
    public void setUpdateFormViewName(String updateFormViewName) {
        this.updateFormViewName = updateFormViewName ;
    }

    private String updateViewName = "redirect:/domain/contentlet/show";

    public void setUpdateViewName(String updateViewName) {
        this.updateViewName = updateViewName ;
    }

}